/* vim: set expandtab shiftwidth=4: -*- mode: c++; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: nil -*-
Compositor
An easy-to-use image compositing program.
Copyright (C) 2006 Wesley Crossman
Email: wesley@crossmans.net

You can redistribute and/or modify this software under the terms of the GNU
General Public License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this
program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA */

#undef VALIDATE

#include "File.h"
#ifdef VALIDATE
extern "C" {
    #include "libxml/parser.h"
    #include "libxml/tree.h"
    #include "libxml/relaxng.h"
}
#endif
#include <QApplication>
#include <QList>
#include <QPainter>
#include <QImage>
#include <QtXml>
#include <iostream>
using namespace std;

void processXmlFile(QString filename);

void myMessageOutput(QtMsgType type, const char *msg);
void myMessageOutput(QtMsgType type, const char *msg) {
    switch (type) {
    case QtDebugMsg:
        cout << "Debug: " << msg << endl;
        break;
    case QtWarningMsg:
        cout << msg << endl;
        break;
    case QtCriticalMsg:
        cout << msg << endl;
        break;
    case QtFatalMsg:
        cout << msg << endl;
        abort();
    }
}

int main(int argc, char *argv[]) {
#ifdef VALIDATE
    LIBXML_TEST_VERSION
#endif
    qInstallMsgHandler(myMessageOutput);
    QApplication app(argc, argv);
    QCoreApplication::setOrganizationName("Wesley Crossman");
    QCoreApplication::setApplicationName("Compositor");
    if (app.arguments().contains("-h")
                || app.arguments().contains("/h")) {
        cout << "Usage: compositor [OPTION] [FILE]..." << endl;
        cout << "Composite images by reading XML FILE(s)." << endl;
        cout << endl;
        cout << "  -d     show output useful for debugging scripts" << endl;
        cout << "  -h     display this help" << endl;
        cout << endl;
        cout << "(C) 2006 Wesley Crossman" << endl;
        cout << "Website: http://code.google.com/p/compositor" << endl;
        cout << "This program is licensed under the GPL 2 or later." << endl;
    }
    for (int a=1;a<app.arguments().size();++a) {
        if (QRegExp("[^-/].*").exactMatch(app.arguments()[a])) {
            processXmlFile(app.arguments()[a]);
        }
    }
#ifdef VALIDATE
    xmlCleanupParser();
#endif
    return 0;
}

void processXmlFile(QString filename) {
    //load xml file
    QDomDocument doc;
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly)) {
        cout << "Error: The XML file \"" << filename.toStdString()
                << "\" could not be opened." << endl;
        return;
    }
    QString errorMsg;
    if (!doc.setContent(file.readAll(), true, &errorMsg)) {
        cout << "Error: The XML file \"" << filename.toStdString()
                << "\" has invalid syntax. ("
                << errorMsg.toStdString() << ")" << endl;
        return;
    }
    file.close();

#ifdef VALIDATE
    xmlDocPtr xmlDoc;
    xmlDoc = xmlReadFile(filename.toAscii(), NULL, 0);
    if (xmlDoc == NULL) {
        cout << "Error: The XML file \"" << filename.toStdString()
                << "\" failed to parse." << endl;
	return;
    }
    xmlRelaxNGParserCtxtPtr parserCtxt = xmlRelaxNGNewParserCtxt(
            QFileInfo(QCoreApplication::arguments()[0]).absoluteDir().filePath("compositor.xml").toAscii());
    assert(parserCtxt);
    xmlRelaxNGSetParserErrors(parserCtxt,
            (xmlRelaxNGValidityErrorFunc)fprintf, (xmlRelaxNGValidityErrorFunc)fprintf,
            (void *)stderr);
    xmlRelaxNGPtr ptrCtxt = xmlRelaxNGParse(parserCtxt);
    assert(ptrCtxt);
    xmlRelaxNGValidCtxtPtr ctxt = xmlRelaxNGNewValidCtxt(ptrCtxt);
    assert(ctxt);
    int invalid = xmlRelaxNGValidateDoc(ctxt, xmlDoc);
    if (invalid) {
        cout << "Error: The XML file is not valid. See errors above. (Code " << invalid << ")" << endl;
        return;
    }
    xmlRelaxNGFreeValidCtxt(ctxt);
    xmlRelaxNGFree(ptrCtxt);
    xmlRelaxNGFreeParserCtxt(parserCtxt);
    xmlFreeDoc(xmlDoc);
#endif

    cout << "Processing \"" << filename.toStdString() << "\"..." << endl;
    
    //parse file entries
    QDomElement docElem = doc.documentElement();
    QDomNodeList files = docElem.elementsByTagName("file");
    QDomElement defaultFile;
    for (int a=0;a<files.count();++a) {
        QDomElement file = files.at(a).toElement();
        new File(file);
    }
    if (!File::fileMap.contains("default")) {
        new File(File::generateDefaultFileElement());
    }
    foreach (File *file, File::files) {
        file->init();
    }

    //generate images
    cout << "Generating Images..." << endl;
    foreach (File *file, File::files) {
        file->save();
        if (QCoreApplication::instance()->arguments().contains("-d")
                    || QCoreApplication::instance()->arguments().contains("/d")) {
            file->dump();
        }
    }

    //cleanup
    foreach (File *file, File::files) {
        delete file;
    }
    File::files.clear();
    File::fileMap.clear();
}

